package com.swap.business.service.impl;

import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.swap.business.config.CustomizeException;
import com.swap.business.constants.SnmpDeviceDataAcquisition;
import com.swap.business.domain.NetworkInterface;
import com.swap.business.domain.SwapDevice;
import com.swap.business.domain.SwapDeviceInfo;
import com.swap.business.dto.DeviceDto;
import com.swap.business.dto.NetworkDto;
import com.swap.business.dto.SnmpMessageDTO;
import com.swap.business.enums.ManufacturerEnum;
import com.swap.business.service.DeviceDataAcquisitionService;
import com.swap.business.service.NetworkInterfaceService;
import com.swap.common.utils.BigDecimalUtils;
import com.swap.common.utils.DateUtils;
import com.swap.common.utils.uuid.IdUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
public class DeviceDataAcquisitionServiceImpl implements DeviceDataAcquisitionService {

    private static final Logger logger = LoggerFactory.getLogger(DeviceDataAcquisitionServiceImpl.class);

    private SnmpDeviceDataAcquisition snmpConfig;

    @Autowired
    private DeviceServiceImpl deviceService;

    @Autowired
    private DeviceInfoServiceImpl deviceInfoService;

    @Autowired
    private NetworkInterfaceService interfaceService;

    /**
     * 计算带宽占用率-h3c
     *
     * @param snmpInfo
     * @param device
     * @param network
     * @return
     */
    @Override
    public List<NetworkDto> queryBoundBandWidthRateForH3c(SnmpMessageDTO snmpInfo, SwapDevice device, List<NetworkDto> network) {
        this.snmpConfig = new SnmpDeviceDataAcquisition(snmpInfo);

        Map<String, Long> inFlowMap1 = snmpConfig.acquirePortInFlow(device);
        Map<String, Long> outFlowMap1 = snmpConfig.acquirePortOutFlow(device);

        Integer speed = snmpConfig.getMaxSpeed(device);
        return flowAlgorithm(inFlowMap1, outFlowMap1, network, speed);
    }

    @Override
    public List<NetworkDto> queryBoundBandWidthRateForHw(SnmpMessageDTO snmpInfo, SwapDevice device, List<NetworkDto> networkDtos) {
        this.snmpConfig = new SnmpDeviceDataAcquisition(snmpInfo);

        List<NetworkDto> dt = snmpConfig.getMonitorRateForHw(device, networkDtos);

        return dt;
    }

    /**
     * 计算入口包转发错包率
     *
     * @param snmpInfo
     * @return
     */
    @Override
    public List<NetworkDto> queryAllBoundPacketsErrorRate(SnmpMessageDTO snmpInfo, SwapDevice device, List<NetworkDto> portInfo) {
        this.snmpConfig = new SnmpDeviceDataAcquisition(snmpInfo);

        Map<Integer, List<String>> inFlowMap1 = snmpConfig.acquirePortInFlowErrKts(device);
        Map<Integer, List<String>> outFlowMap1 = snmpConfig.acquirePortOutFlowErrKts(device);

        sleepByQueryFlow(); // 停止1秒

        Map<Integer, List<String>> inFlowMap2 = snmpConfig.acquirePortInFlowErrKts(device);
        Map<Integer, List<String>> outFlowMap2 = snmpConfig.acquirePortOutFlowErrKts(device);

        return flowAlgorithmErrKts(inFlowMap1, inFlowMap2, outFlowMap1, outFlowMap2, portInfo);
    }

    /**
     * 获取设备信息
     *
     * @param snmpInfo
     * @param device
     * @return
     * @throws JsonProcessingException
     * @throws CustomizeException
     */
    @Override
    @Async("asyncServiceExecutor")
    public CompletableFuture<Void> queryGetAllDeviceInfo(SnmpMessageDTO snmpInfo, SwapDevice device) throws JsonProcessingException, CustomizeException {
        this.snmpConfig = new SnmpDeviceDataAcquisition(snmpInfo);

        List<CompletableFuture<Void>> futures = new ArrayList<>();
        try {
            //基本信息
            DeviceDto deviceDto = snmpConfig.acquireDeviceInfo(device);
            if (!Objects.equals(deviceDto.getEquipmentUuId(), "") && deviceDto.getNetwork().isEmpty()) {
                throw new CustomizeException("Basic network is empty");
            }
            //带宽占用率
            List<NetworkDto> networks = new ArrayList<>();
            if (device.getManufacturer().contains(ManufacturerEnum.H3C.name())) {
                sleepByQueryFlow(); // 停止1秒
                networks = this.queryBoundBandWidthRateForH3c(snmpInfo, device, deviceDto.getNetwork());
            } else if (device.getManufacturer().contains(ManufacturerEnum.HUAWEI.name()) || device.getManufacturer().contains(ManufacturerEnum.FUTUREMATRIX.name())) {
                networks = this.queryBoundBandWidthRateForHw(snmpInfo, device, deviceDto.getNetwork());
            }
            if (!Objects.equals(deviceDto.getEquipmentUuId(), "") && networks.isEmpty()) {
                throw new CustomizeException("Networks is empty");
            }
            List<NetworkDto> networkDtos = this.queryAllBoundPacketsErrorRate(snmpInfo, device, networks);
            deviceDto.setNetwork(networkDtos);
            if (!Objects.equals(deviceDto.getEquipmentUuId(), "") && deviceDto.getNetwork().isEmpty()) {
                throw new CustomizeException("Network is empty");
            }

            if (!Objects.equals(device.getEquipmentUuId(), deviceDto.getEquipmentUuId())) {
                device.setEquipmentUuId(deviceDto.getEquipmentUuId());
                device.setId(device.getId());
                deviceService.updateById(device);
            }

            //入库
            SwapDeviceInfo swapDeviceInfo = new SwapDeviceInfo();
            swapDeviceInfo.setId(IdUtils.fastSimpleUUID());
            swapDeviceInfo.setDeviceId(device.getId());
            swapDeviceInfo.setEquipmentUuId(deviceDto.getEquipmentUuId());
            swapDeviceInfo.setCpuUsageRate(deviceDto.getCpuUsageRate());
            swapDeviceInfo.setTemp(deviceDto.getTemp());
            swapDeviceInfo.setMemoryUsageRate(deviceDto.getMemoryUsageRate());
            swapDeviceInfo.setFanStatus(deviceDto.getFanStatus());
            swapDeviceInfo.setCollectTime(deviceDto.getCollectTime());

            String zId = swapDeviceInfo.getId();
            boolean val = deviceInfoService.save(swapDeviceInfo);
            for (int i = 0; i < deviceDto.getNetwork().size(); i++) {
                NetworkInterface networkInterface = JSON.parseObject(JSON.toJSONString(deviceDto.getNetwork().get(i)), NetworkInterface.class);
                String id = IdUtils.fastSimpleUUID();
                networkInterface.setId(id);
                networkInterface.setRecordId(zId);
                networkInterface.setDeviceId(device.getId());
                networkInterface.setEquipmentUuId(deviceDto.getEquipmentUuId());
                networkInterface.setRecTime(DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, deviceDto.getNetwork().get(i).getRecTime()));
//            networkInterface.setUpdateTime(DateUtils.getSysTimeInfo());
                networkInterface.setUpdateTime(DateUtils.getNowDate());
                interfaceService.save(networkInterface);
            }
            device.setDeviceArea("ok");
            device.setId(device.getId());
            deviceService.updateById(device);
            return CompletableFuture.completedFuture(null);
        } catch (CustomizeException e) {
            // 获取异常的详细信息
            StringWriter sw = new StringWriter();
            e.printStackTrace(new PrintWriter(sw));
            String detailedMessage = sw.toString();

            logger.warn("[{}]执行异常, IP:[{}], 团体名:[{}], 厂家:[{}], ID编号:[{}], 错误详情:[{}]", device.getDeviceName(), device.getDeviceIp(), device.getCommunityRead(), device.getManufacturer(), device.getId(), detailedMessage);

            device.setDeviceArea("fail");
            device.setId(device.getId());
            deviceService.updateById(device);

            CompletableFuture<Void> future = new CompletableFuture<>();
            future.completeExceptionally(e);
            return future;
        } catch (Exception e) {
            // 获取异常的详细信息
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            e.printStackTrace(pw);
            String detailedMessage = sw.toString();

            logger.error("[{}]执行失败, IP:[{}], 团体名:[{}], 厂家:[{}], ID编号:[{}], 错误详情:[{}]", device.getDeviceName(), device.getDeviceIp(), device.getCommunityRead(), device.getManufacturer(), device.getId(), detailedMessage);

            device.setDeviceArea("fail");
            device.setId(device.getId());
            deviceService.updateById(device);

            CompletableFuture<Void> future = new CompletableFuture<>();
            future.completeExceptionally(e);
            return future;
        }
    }

    /**
     * 采集流量及算方式
     *
     * @param flow1 第二次采集的数据
     * @return 计算之后的流量数据
     */
    private List<NetworkDto> flowAlgorithm(Map<String, Long> flow1, Map<String, Long> outFlowMap1, List<NetworkDto> networkDtos, Integer speed) {
        ArrayList<NetworkDto> resultList = new ArrayList<>();
        int count = 0;
        for (String key : flow1.keySet()) {
            NetworkDto networkDto = networkDtos.get(count);
            count++;
            Long value1 = flow1.get(key);
            Long outValue1 = outFlowMap1.get(key);
//            Long value2 = flow2.get(key);
            networkDto.setInboundBandwidthRate(BigDecimalUtils.getRateDecimal((value1 - networkDto.getInboundTraffic()) * 8, 1000L * 1024 * 1024));
            networkDto.setOutboundBandwidthRate(BigDecimalUtils.getRateDecimal((outValue1 - networkDto.getOutboundTraffic()) * 8, 1000L * 1024 * 1024));

            resultList.add(networkDto);
        }
        return resultList;
    }

    /**
     * 转发错包率及算方式
     *
     * @param inFlow1
     * @param outFlow1
     * @param portInfo
     * @return
     */
    private List<NetworkDto> flowAlgorithmErrKts(Map<Integer, List<String>> inFlow1, Map<Integer, List<String>> inFlow2, Map<Integer, List<String>> outFlow1, Map<Integer, List<String>> outFlow2, List<NetworkDto> portInfo) {
        ArrayList<NetworkDto> resultList = new ArrayList<>();

        int count = 0;
        for (Integer key : inFlow1.keySet()) {
            List<String> inValList1 = inFlow1.get(key);
            List<String> inValList2 = inFlow2.get(key);
            List<String> outValList1 = outFlow1.get(key);
            List<String> outValList2 = outFlow2.get(key);
            if (inValList1.get(0).startsWith(SnmpDeviceDataAcquisition.PORT_ERROR_NAME) || inValList1.get(0).startsWith(SnmpDeviceDataAcquisition.PORT_ERROR_NAME1) || inValList1.get(0).startsWith(SnmpDeviceDataAcquisition.PORT_ERROR_NAME2) || inValList2.get(0).startsWith(SnmpDeviceDataAcquisition.PORT_ERROR_NAME) || inValList2.get(0).startsWith(SnmpDeviceDataAcquisition.PORT_ERROR_NAME1) || inValList2.get(0).startsWith(SnmpDeviceDataAcquisition.PORT_ERROR_NAME2)) {
                if (!inValList1.get(0).contains(".")) {
                    NetworkDto portInfoDto = portInfo.get(count);
                    count++;
                    long ifInErrorsT1 = Long.parseLong(inValList1.get(1));   //t1时刻入方向错包数
                    long ifHcInUcastPktsT1 = Long.parseLong(inValList1.get(2));  //t1时刻入方向单播报文数
                    long ifHcInMulticastPktsT1 = Long.parseLong(inValList1.get(3));  //t1时刻口入方向组播报文数
                    long ifHCInBroadcastPktsT1 = Long.parseLong(inValList1.get(4));  //t1时刻口入方向广播报文数

                    long ifInErrorsT2 = Long.parseLong(inValList2.get(1));   //t2时刻入方向错包数
                    long ifHcInUcastPktsT2 = Long.parseLong(inValList2.get(2));  //t2时刻入方向单播报文数
                    long ifHcInMulticastPktsT2 = Long.parseLong(inValList2.get(3));  //t2时刻口入方向组播报文数
                    long ifHCInBroadcastPktsT2 = Long.parseLong(inValList2.get(4));  //t2时刻口入方向广播报文数

                    long ifOutErrorsT1 = Long.parseLong(outValList1.get(1));   //t1时刻出方向错包数
                    long ifHcOutUcastPktsT1 = Long.parseLong(outValList1.get(2));  //t1时刻出方向单播报文数
                    long ifHcOutMulticastPktsT1 = Long.parseLong(outValList1.get(3));  //t1时刻口出方向组播报文数
                    long ifHCOutBroadcastPktsT1 = Long.parseLong(outValList1.get(4));  //t1时刻口出方向广播报文数

                    long ifOutErrorsT2 = Long.parseLong(outValList2.get(1));   //t2时刻出方向错包数
                    long ifHcOutUcastPktsT2 = Long.parseLong(outValList2.get(2));  //t2时刻出方向单播报文数
                    long ifHcOutMulticastPktsT2 = Long.parseLong(outValList2.get(3));  //t2时刻口出方向组播报文数
                    long ifHCOutBroadcastPktsT2 = Long.parseLong(outValList2.get(4));  //t2时刻口出方向广播报文数


                    Long rate1 = ifInErrorsT1 - ifInErrorsT2 + ifHcInUcastPktsT1 - ifHcInUcastPktsT2 + ifHcInMulticastPktsT1 - ifHcInMulticastPktsT2 + ifHCInBroadcastPktsT1 - ifHCInBroadcastPktsT2;
                    portInfoDto.setInboundPacketsErrorRate(BigDecimalUtils.getRateDecimal(ifInErrorsT1 - ifInErrorsT2, rate1));

                    Long rate2 = ifOutErrorsT1 - ifOutErrorsT2 + ifHcOutUcastPktsT1 - ifHcOutUcastPktsT2 + ifHcOutMulticastPktsT1 - ifHcOutMulticastPktsT2 + ifHCOutBroadcastPktsT1 - ifHCOutBroadcastPktsT2;
                    portInfoDto.setOutboundPacketsErrorRate(BigDecimalUtils.getRateDecimal(ifOutErrorsT1 - ifOutErrorsT2, rate2));

                    resultList.add(portInfoDto);
                }
            }
        }
        return resultList;
    }

    /**
     * 调用Thread.sleep()方法
     */
    private void sleepByQueryFlow() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
